/*****************************
 * Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009
 * (Ported to C# by Jake Tober 2010)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. 
 *
 * Commercial licensing is available under a separate license, please
 * contact author for details.
 *
 * Source code is available at http://rscode.sourceforge.net
 * 
 *
 * Multiplication and Arithmetic on Galois Field GF(256)
 *
 * From Mee, Daniel, "Magnetic Recording, Volume III", Ch. 5 by Patel.
 * 
 *
 ******************************/
using System.Diagnostics.Contracts;
namespace RSCode
{
    internal class Galois
    {
        public Galois()
        {
            init_exp_table();
        }

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(gexp != null);
            Contract.Invariant(glog != null);
            Contract.Invariant(gexp.Length == gexp_length);
            Contract.Invariant(glog.Length == glog_length);
        }

        public const int gexp_length = 512;
        public const int glog_length = 256;

        byte[] gexp = new byte[gexp_length];
        byte[] glog = new byte[glog_length];

        public byte gexp_lookup(int index)
        {
            Contract.Requires(index >= 0);
            Contract.Requires(index < gexp_length);
            return gexp[index]; 
        }

        public byte glog_lookup(int index)
        {
            Contract.Requires(index >= 0);
            Contract.Requires(index < glog_length);
            return glog[index]; 
        }

        /* initialize the table of powers of alpha */
        void init_exp_table()
        {
            int i;
            byte z;
            byte pinit, p1, p2, p3, p4, p5, p6, p7, p8;

            pinit = p2 = p3 = p4 = p5 = p6 = p7 = p8 = 0;
            p1 = 1;

            gexp[0] = 1;
            gexp[255] = gexp[0];
            glog[0] = 0;			/* shouldn't log[0] be an error? */

            for (i = 1; i < 256; i++)
            {
                pinit = p8;
                p8 = p7;
                p7 = p6;
                p6 = p5;
                p5 = (byte)(p4 ^ pinit);
                p4 = (byte)(p3 ^ pinit);
                p3 = (byte)(p2 ^ pinit);
                p2 = p1;
                p1 = pinit;
                gexp[i] = (byte)(p1 + p2 * 2 + p3 * 4 + p4 * 8 + p5 * 16 + p6 * 32 + p7 * 64 + p8 * 128);
                gexp[i + 255] = gexp[i];
            }

            for (i = 1; i < 256; i++)
            {
                for (z = 0; z <= 255; z++)
                {
                    if (gexp[z] == i)
                    {
                        glog[i] = z;
                        break;
                    }
                }
            }
        }

        /* multiplication using logarithms */
        public byte gmult(int a, int b)
        {
            Contract.Requires(a >= 0);
            Contract.Requires(a < glog_length);
            Contract.Requires(b >= 0);
            Contract.Requires(b < glog_length);

            int i, j;
            if (a == 0 || b == 0) return (0);
            i = glog[a];
            j = glog[b];
            return (gexp[i + j]);
        }

        public int ginv(int elt)
        {
            Contract.Requires(elt >= 0);
            Contract.Requires(elt < glog_length);

            return (gexp[255 - glog[elt]]);
        }
    }
}